﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using Seagull.Configuration;
using Seagull.DAL.Utils;
using Seagull.Events;
using Seagull.Generic;
using Seagull.Model;
using Seagull.Model.Alerts;

namespace Seagull.DAL
{
    public class FrameReader : IDisposable
    {
        private readonly IPhaseShifter _phaseShifter;

        private readonly FrameDataCoercer _frameDataCoercer;

        private readonly BinaryReader _reader;

        public FrameReader(Stream inputStream,
            IPhaseShifter phaseShifter = null,
            FrameDataCoercer frameDataCoercer = null)
        {
            _phaseShifter = phaseShifter;
            _frameDataCoercer = frameDataCoercer;
            _reader = new BinaryReader(inputStream);
        }

        /// <summary>
        /// 读取并返回帧，直到没有更多的帧。
        /// </summary>
        /// <returns></returns>
        public IEnumerable<Frame> ReadToEnd()
        {
            Optional<Frame> frame;
            while((frame = Read()).HasValue)
            {
                yield return frame.Value;
            }
        }

        // TODO: Merge with BlockRead()
        /// <summary>
        /// 读取1帧并返回。若已经没有更多的帧则返回<see cref="Optional.None"/>。
        /// </summary>
        /// <returns></returns>
        public Optional<Frame> Read()
        {
            while(true)
            {
                try
                {
                    // 前进至帧头并且读取
                    var success = SkipUntilFrameHead();
                    if(!success)
                        return Optional.None;

                    // 开始读取一帧
                    if(_reader.BaseStream.Position < _reader.BaseStream.Length)
                    {
                        _reader.BaseStream.Seek(3, SeekOrigin.Current);

                        var timestamp = TimestampAccessor.Read(_reader);
                        var frame = new Frame(timestamp);
                        _reader.BaseStream.Seek(2, SeekOrigin.Current);
                        frame.Data = _reader.ReadBytes(frame.Data.Length);

                        ProcessFrameData(frame);
                        _reader.BaseStream.Seek(5, SeekOrigin.Current);
                        return Optional.Some(frame);
                    }
                    return Optional.None;
                }
                catch(ArgumentOutOfRangeException)
                {
                    // 跳过这一帧
                    continue;
                }
            }
        }

        private void ProcessFrameData(Frame frame)
        {
            // 使用相位转换器进行相位转换
            if(_phaseShifter != null)
            {
                _phaseShifter.Shift(frame.Data);
            }

            // 使用帧数值强制器修正数据
            if(_frameDataCoercer != null)
            {
                _frameDataCoercer.Coerce(frame.Data);
            }
        }

        /// <summary>
        /// 读取1帧并返回。若当前流上没有足够的数据则会阻塞线程的执行。需要底层流的读取也是阻塞的。
        /// </summary>
        /// <returns></returns>
        public Frame BlockRead()
        {
            while(true)
            {
                try
                {
                    // 前进至帧头并且读取
                    SkipUntilFrameHead();
                    // 帧类别
                    var frameType = _reader.ReadByte();
                    if(!Frame.IsValidFrameType(frameType))
                    {
                        continue;
                    }

                    // 通道
                    var channel = _reader.ReadByte();
                    if(frameType == (byte)FrameType.Phase)
                    {
                        channel = Channel.PhaseChannel;
                    }
                    else if(channel < Channel.MinChannel && channel != Channel.PhaseChannel)
                    {
                        continue;
                    }

                    // 帧序
                    var index = _reader.ReadByte();

                    // 时间戳
                    var timestamp = TimestampAccessor.Read(_reader);

                    //数据长度
                    var length = _reader.ReadInt16();
                    if(length != Frame.DataLength)
                    {
                        continue;
                    }

                    // 数据
                    var data = _reader.ReadBytes(Frame.DataLength);

                    var misc = _reader.ReadInt32();

                    var tail = _reader.ReadByte();
                    if(tail != Frame.Tail)
                    {
                        continue;
                    }

                    var frame = new Frame(channel, timestamp, data);
                    ProcessFrameData(frame);
                    return frame;
                }
                catch(ArgumentOutOfRangeException)
                {
                    // 日期时间格式不对，跳过
                    continue;
                }
                catch(IOException exception)
                {
                    // 出错，跳过
                    LogOnlyAlertEvent.Instance.Publish(new ApplicationAlert(exception, "读取帧"));
                    continue;
                }
            }
        }

        /// <summary>
        /// 读取帧并将读取的帧作为参数调用<paramref name="stopCondition"/>，直到其返回<c>false</c>
        /// 或没有更多的帧。
        /// </summary>
        /// <param name="stopCondition"></param>
        public void ReadUntil(Func<Frame, bool> stopCondition)
        {
            Optional<Frame> frame;
            while((frame = Read()).HasValue)
            {
                if(stopCondition(frame.Value))
                {
                    Rewind();
                    return;
                }
            }
        }

        public void Dispose()
        {
            if(_reader != null)
                _reader.Close();
        }

        /// <summary>
        /// 依次从流里读取字节，直到读到一个帧头并返回<c>true</c>. 
        /// 当方法返回时，帧头已经被读取了。
        /// 若到达流的末尾依然没有读取到帧头则返回<c>false</c>.
        /// </summary>
        /// <returns></returns>
        private bool SkipUntilFrameHead()
        {
            while(true)
            {
                var value = _reader.BaseStream.ReadByte();
                if(value == -1)
                {
                    return false;
                }
                if(value == Frame.Head)
                {
                    return true;
                }
            }
        }

        /// <summary>
        /// 将文件当前读取位置后退一个帧的大小。
        /// </summary>
        private void Rewind()
        {
            _reader.BaseStream.Seek(-Frame.FrameSize, SeekOrigin.Current);
        }
    }
}
